/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.platformservice.persistence.basic;

import org.proteusframework.core.api.IProteusConstants;
import org.proteusframework.core.api.config.IConfigPropertyPrototype;
import org.proteusframework.core.base.Namespace;
import org.proteusframework.core.base.config.ConfigPropertyBuilder;
import org.proteusframework.platformservice.IExtendedPlatformDelegate;
import org.proteusframework.platformservice.appconfig.IAppConfigService;
import org.proteusframework.platformservice.persistence.api.IMessageBeanManager;
import org.proteusframework.platformservice.persistence.api.IPackageManager;
import org.proteusframework.platformservice.persistence.api.IProjectManager;
import org.proteusframework.platformservice.persistence.api.IVFSManager;
import org.proteusframework.platformservice.persistence.base.AbstractPersistenceService;
import org.proteusframework.platformservice.persistence.messagebean.NullMessageBeanManager;
import org.proteusframework.platformservice.persistence.packaging.DefaultPackageManager;
import org.proteusframework.platformservice.persistence.project.NullProjectManager;
import org.proteusframework.platformservice.persistence.vfs.NullVFSManager;

import java.util.logging.Logger;

/**
 * @author Tacoma Four
 */
public class DefaultPersistenceService extends AbstractPersistenceService
{
    private static final Logger logger = Logger.getLogger(DefaultPersistenceService.class.getCanonicalName());

    /**
     * Key used by {@link org.proteusframework.platformservice.appconfig.IAppConfigService} implementations to
     * declare an optional DB configuration metadata property that may be used by a concrete persistence service.
     */
    public static final String KEY_DB_CONFIG = "db.config";

    /**
     * Configuration property prototype for the DB configuration properties file.
     */
    public static final IConfigPropertyPrototype PROPERTY_DB_CONFIG
            = new ConfigPropertyBuilder(KEY_DB_CONFIG)
            .setDefaultValue(DefaultMessageBeanManager.DERBY_CONFIG_RESOURCE_PATH)
            .setDescription("Defines a .properties file that defines database query statements and supported data types")
            .setMandatory(false)
            .setExample(DefaultMessageBeanManager.DERBY_CONFIG_RESOURCE_PATH)
            .buildPrototype();

    /**
     * Key used by {@link org.proteusframework.platformservice.appconfig.IAppConfigService} implementation to
     * declare a specific {@link IMessageBeanManager} class that should provide message bean management for the
     * persistence service.
     * <p/>
     * If omitted, an instance of <code>DefaultMessageBeanManager</code> is used, if available via the CLASSPATH.
     */
    public static final String KEY_MESSAGE_BEAN_MANAGER_CLASS = "messageBean.manager";

    /**
     * Configuration property prototype for the message bean manager implementation.
     */
    public static final IConfigPropertyPrototype PROPERTY_MESSAGE_BEAN_MANAGER
            = new ConfigPropertyBuilder(KEY_MESSAGE_BEAN_MANAGER_CLASS)
            .setDefaultValue("org.proteusframework.platformservice.persistence.basic.DefaultMessageBeanManager")
            .setDescription("Defines the message bean manager responsible for message store management")
            .setMandatory(false)
            .setExample("org.proteusframework.platformservice.persistence.basic.DefaultMessageBeanManager")
            .buildPrototype();

    /**
     * Key used by {@link org.proteusframework.platformservice.appconfig.IAppConfigService} implementation to
     * declare a specific {@link IVFSManager} class that should provide virtual file system management for the
     * persistence service.
     * <p/>
     * If omitted, an instance of <code>DefaultVFSManager</code> is used, if available via the CLASSPATH.
     */
    public static final String KEY_VFS_MANAGER_CLASS = "vfs.manager";

    /**
     * Configuration property prototype for the VFS manager implementation.
     */
    public static final IConfigPropertyPrototype PROPERTY_VFS_MANAGER
            = new ConfigPropertyBuilder(KEY_VFS_MANAGER_CLASS)
            .setDefaultValue("org.proteusframework.platformservice.persistence.basic.DefaultVFSManager")
            .setDescription("Defines the VFS manager responsible for the virtual file system")
            .setMandatory(false)
            .setExample("org.proteusframework.platformservice.persistence.basic.DefaultVFSManager")
            .buildPrototype();

    /**
     * Key used by {@link org.proteusframework.platformservice.appconfig.IAppConfigService} implementation to
     * declare a specific {@link IProjectManager} class that should provide project management for the
     * persistence service.
     * <p/>
     * If omitted, an instance of <code>DefaultProjectManager</code> is used, if available via the CLASSPATH.
     */
    public static final String KEY_PROJECT_MANAGER_CLASS = "project.manager";

    /**
     * Configuration property prototype for the project manager implementation.
     */
    public static final IConfigPropertyPrototype PROPERTY_PROJECT_MANAGER
            = new ConfigPropertyBuilder(KEY_PROJECT_MANAGER_CLASS)
            .setDefaultValue("org.proteusframework.platformservice.persistence.basic.DefaultProjectManager")
            .setDescription("Defines the project manager responsible for projects")
            .setMandatory(false)
            .setExample("org.proteusframework.platformservice.persistence.basic.DefaultProjectManager")
            .buildPrototype();


    /**
     * Key used by {@link org.proteusframework.platformservice.appconfig.IAppConfigService} implementation to
     * declare a specific {@link IPackageManager} class that should provide package management for the
     * persistence service.
     * <p/>
     * If omitted, an instance of <code>DefaultPackageManager</code> is used, if available via the CLASSPATH.
     */
    public static final String KEY_PACKAGE_MANAGER_CLASS = "package.manager";

    /**
     * Configuration property prototype for the package manager implementation.
     */
    public static final IConfigPropertyPrototype PROPERTY_PACKAGE_MANAGER = new ConfigPropertyBuilder(KEY_PACKAGE_MANAGER_CLASS)
            .setDefaultValue("org.proteusframework.platformservice.persistence.packaging.DefaultPackageManager")
            .setDescription("Defines the package manager used to process packaging requests")
            .setMandatory(false)
            .setExample("org.proteusframework.platformservice.persistence.packaging.DefaultPackageManager")
            .buildPrototype();


    private IAppConfigService appCfg;

    public DefaultPersistenceService()
    {
        super(new Namespace(IProteusConstants.NAMESPACE, "DefaultPersistenceService"), true);
        registerConfigPropertyPrototypes();
    }

    private void registerConfigPropertyPrototypes()
    {
        addConfigurationPropertyPrototype(PROPERTY_DB_CONFIG);
        addConfigurationPropertyPrototype(PROPERTY_MESSAGE_BEAN_MANAGER);
        addConfigurationPropertyPrototype(PROPERTY_VFS_MANAGER);
        addConfigurationPropertyPrototype(PROPERTY_PROJECT_MANAGER);
        addConfigurationPropertyPrototype(PROPERTY_PACKAGE_MANAGER);
    }

    protected DefaultPersistenceService(String serviceName)
    {
        super(new Namespace(IProteusConstants.NAMESPACE, serviceName), false);
        registerConfigPropertyPrototypes();
    }

    @Override
    protected void onDelegateInjected()
    {
        IExtendedPlatformDelegate extendedDelegate = delegate.adapt(IExtendedPlatformDelegate.class);
        appCfg = extendedDelegate.getAppConfigService();

        // Create a real project manager
        createProjectManager(false);

        // Until a project is opened, we don't need a real message bean messageBeanManager
        createMessageBeanManager(true);
        createVFSManager(true);
        createPackageManager(true);
    }

    protected void createProjectManager(boolean createNullManager)
    {
        IProjectManager projectManager = null;

        if (createNullManager)
        {
            projectManager = new NullProjectManager();
        } else
        {
            String projMgrClass = appCfg.getMetadata().getProperty(KEY_PROJECT_MANAGER_CLASS);

            if (projMgrClass != null)
            {
                try
                {
                    logger.config("Instantiating IProjectManager from class " + projMgrClass);

                    Class clazz = Class.forName(projMgrClass);

                    if (null != clazz)
                    {
                        if (IProjectManager.class.isAssignableFrom(clazz))
                        {
                            projectManager = (IProjectManager) clazz.newInstance();
                            logger.config("IProjectManager setup successfully from class " + projMgrClass);
                        } else
                        {
                            logger.severe("IProjectManager class declared in IAppConfigService is not assignable from IProjectManager");
                        }
                    } else
                    {
                        logger.severe("Unable to locate class " + projMgrClass);
                    }
                } catch (Exception e)
                {
                    logger.severe("IProjectManager class declared in IAppConfigService could not be loaded: " + e.getMessage());
                }
            }

            if (null == projectManager)
            {
                logger.config("Creating instance of default IProjectManager");
                projectManager = new DefaultProjectManager();
            }
        }

        injectProjectManager(projectManager);
    }

    protected void createPackageManager(boolean createDefaultManager)
    {
        IPackageManager packageManager = null;

        if (createDefaultManager)
        {
            packageManager = new DefaultPackageManager();
        } else
        {
            String pkgClass = appCfg.getMetadata().getProperty(KEY_PACKAGE_MANAGER_CLASS);

            if (pkgClass != null)
            {
                try
                {
                    logger.config("Instantiating IPackageManager from class " + pkgClass);

                    Class clazz = Class.forName(pkgClass);

                    if (null != clazz)
                    {
                        if (IPackageManager.class.isAssignableFrom(clazz))
                        {
                            packageManager = (IPackageManager) clazz.newInstance();
                            logger.config("IPackageManager setup successfully from class " + pkgClass);
                        } else
                        {
                            logger.severe("IPackageManager class declared in IAppConfigService is not assignable from IPackageManager");
                        }
                    } else
                    {
                        logger.severe("Unable to locate class " + pkgClass);
                    }
                } catch (Exception e)
                {
                    logger.severe("IPackageManager class declared in IAppConfigService could not be loaded: " + e.getMessage());
                }
            }

            if (null == packageManager)
            {
                logger.config("Creating instance of default IPackageManager");
                packageManager = new DefaultPackageManager();
            }
        }

        injectPackageManager(packageManager);
    }

    protected void createVFSManager(boolean createNullManager)
    {
        IVFSManager vfsManager = null;

        if (createNullManager)
        {
            vfsManager = new NullVFSManager();
        } else
        {
            String vfsClass = appCfg.getMetadata().getProperty(KEY_VFS_MANAGER_CLASS);

            if (vfsClass != null)
            {
                try
                {
                    logger.config("Instantiating IVFSManager from class " + vfsClass);

                    Class clazz = Class.forName(vfsClass);

                    if (null != clazz)
                    {
                        if (IVFSManager.class.isAssignableFrom(clazz))
                        {
                            vfsManager = (IVFSManager) clazz.newInstance();
                            logger.config("IVFSManager setup successfully from class " + vfsClass);
                        } else
                        {
                            logger.severe("IVFSManager class declared in IAppConfigService is not assignable from IVFSManager");
                        }
                    } else
                    {
                        logger.severe("Unable to locate class " + vfsClass);
                    }
                } catch (Exception e)
                {
                    logger.severe("IVFSManager class declared in IAppConfigService could not be loaded: " + e.getMessage());
                }
            }

            if (null == vfsManager)
            {
                logger.config("Creating instance of default IVFSManager");
                vfsManager = new DefaultVFSManager();
            }
        }

        injectVFSManager(vfsManager);
    }

    protected void createMessageBeanManager(boolean createNullManager)
    {
        IMessageBeanManager messageBeanManager = null;

        if (createNullManager)
        {
            messageBeanManager = new NullMessageBeanManager();
        } else
        {
            String registrarClass = appCfg.getMetadata().getProperty(KEY_MESSAGE_BEAN_MANAGER_CLASS);

            if (registrarClass != null)
            {
                try
                {
                    logger.config("Instantiating IMessageBeanManager from class " + registrarClass);

                    Class clazz = Class.forName(registrarClass);

                    if (null != clazz)
                    {
                        if (IMessageBeanManager.class.isAssignableFrom(clazz))
                        {
                            messageBeanManager = (IMessageBeanManager) clazz.newInstance();
                            logger.config("IMessageBeanManager setup successfully from class " + registrarClass);
                        } else
                        {
                            logger.severe("IMessageBeanManager class declared in IAppConfigService is not assignable from IMessageBeanManager");
                        }
                    } else
                    {
                        logger.severe("Unable to locate class " + registrarClass);
                    }
                } catch (Exception e)
                {
                    logger.severe("IMessageBeanManager class declared in IAppConfigService could not be loaded: " + e.getMessage());
                }
            }

            if (null == messageBeanManager)
            {
                logger.config("Creating instance of default IMessageBenManager");
                messageBeanManager = new DefaultMessageBeanManager();
            }
        }
        injectMessageBeanManager(messageBeanManager);
    }

    @Override
    public void start()
    {
        logger.config("DefaultPersistenceService has started");
    }

    @Override
    public void stop()
    {
        logger.config("DefaultPersistenceService has stopped");
    }
}
